#include <iostream>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>


#define FORMAT "[%s@%s %s]# " // 命令行提示符格式
const size_t COMMAND_SIZE = 1024;


// 命令行参数表
const int MAXARGC = 128;// 最大参数个数
char* g_argv[MAXARGC];// 命令行参数表
int g_argc = 0;


// 环境变量表
const int MAXENVS = 128;
char* g_env[MAXENVS];
int g_envs = 0;


// for test
char cwd[1024];
char cwdenv[1024];


// 最新退出码
int lastcode = 0;

// 初始化环境变量
void initEnviron()
{
    // 1.从配置文件中读取环境变量
    extern char** environ;
    for(int i=0; environ[i]; ++i)
    {
        g_env[i] = (char*)malloc(strlen(environ[i]));
        strcpy(g_env[i], environ[i]);
        g_envs++;
    }

    // 2.putnev将环境变量加载到进程地址空键
    for(int i=0; g_env[i]; ++i)
    {
        putenv(g_env[i]);
    }

    // 让全局指针指向环境变量表
    environ = g_env;
}


// 获取用户名
char* getUserName()
{
    return getenv("USER");
}


// 获取主机名
char* getHostName()
{
    return getenv("HOSTNAME");
}


// 获取当前工作目录
char* getPwd()
{
    const char* pwd = getcwd(cwd,sizeof(cwd));
    if(pwd !=NULL)
    {
        snprintf(cwdenv, sizeof(cwdenv),"PWD=%s", pwd);
        putenv(cwdenv);
    }
    return (char*)pwd;
    //return getenv("PWD");
}


// 提取出当前的工作目录
std::string getDir(char* pwd)
{
    std::string dir(pwd);
    std::size_t pos = dir.rfind('/',std::string::npos);
    if(pos == std::string::npos) return "BUG?";
    return dir.substr(pos+1);
}


// 制作命令行提示符
void makeCommandLine(char* prompt, size_t size)
{
    snprintf(prompt, size, FORMAT, getUserName(), getHostName(),getDir(getPwd()).c_str());
    //snprintf(prompt, size, FORMAT, getUserName(), getHostName(),getPwd());
}


// 打印命令行提示符
void printCommandLinePrompt()
{
    char prompt[COMMAND_SIZE];
    makeCommandLine(prompt, sizeof(prompt));    
    printf("%s",prompt);
    fflush(stdout);
}


// 获取命令字符串
bool getUserCommand(char* command_list, size_t size)
{
    char* s = fgets(command_list, size, stdin); // 获取用户输入的命令
    if(s == NULL) return false; // 如果用户只输入了回车，则返回输入失败
    s[strlen(command_list)-1] = '\0';
    return true;
}


// 命令行解释，以空格作为分隔符，分割命令
void commandParse(char* command)
{
    g_argc = 0;
    g_argv[g_argc++] = strtok(command, " ");
    while((bool)(g_argv[g_argc++] = strtok(NULL, " "))) ;
    g_argc--;
}


// cd命令
void Cd()
{
    if(g_argc == 1)
    {
        chdir(getenv("HOME"));
    }
    else
    {
        std::string adderss = g_argv[1];
        if(adderss == "~")
        {
            chdir(getenv("HOME"));
        }
        else if(adderss == "-")
        {
            // todo
        }
        else
        {
            chdir(adderss.c_str());
        }
    }
}


// echo 命令
void Echo()
{
    if(g_argc == 2)
    {
        std::string text = g_argv[1];
        if(text == "$?")
        {
            std::cout << lastcode << std::endl;
            lastcode = 0;
        }
        else if(text[0] == '$')
        {
            std::string p = text.substr(1);
            char* env_value = getenv(p.c_str());
            if(env_value)
                std::cout << env_value << std::endl;
            else
                std::cout << std::endl;
        }
        else
        {
            std::cout << text << std::endl;
        }
    }
}


// 判断并执行内建命令
bool checkAndExecuteBulidInCommand()
{
    std::string com = g_argv[0];
    if(com == "cd")
    {
        Cd();
        return true;
    }
    else if(com == "echo")
    {
        Echo();
        return true;
    }
    else if(com == "alias")
    {
        //Alias();
        return true;
    }

    return false;
}


// 程序替换，执行命令
void Execute()
{
    pid_t id = fork();
    if(id == 0)
    {
        // child
        execvp(g_argv[0], g_argv);
        exit(1);
    }

    // father
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if(rid > 0)
    {
        // 等待成功
        lastcode = WEXITSTATUS(status);
    }
}


int main()
{
    // 初始化环境变量
    initEnviron();

    // 命令行解释器是一直在执行的
    while(true)
    {
        // 1. 打印命令行解释器
        printCommandLinePrompt();

        // 2.获取用户输入的命令
        char command_list[COMMAND_SIZE];
        if(!getUserCommand(command_list, sizeof(command_list)))
            continue;

        // 3.切割命令
        commandParse(command_list);

        // 4. 判断并执行内置命令
        if(checkAndExecuteBulidInCommand())
            continue;
        
        // 5.执行命令
        Execute();
    }
    return 0;
}
